Disaster Relief Project

K-Folds Out of Sampling Performance ( + RF/SVM )

Method KNN(k=5) LDA QDA LR RF(tuning parameter: mtry=3; ntrees=500) SVM(tuning parameter: cost=10; gamma=1; sigma = 8.691262; C = 1)
Accuracy 99.6% 98.3% 99.4% 99.5% 99.7% 99.7%
AUC 99.8% 98.7% 99.8% 99.8% 99.0% 99.4%
ROC in tabs in tabs in tabs in tabs in tabs in tabs
Threshold .66 .85 .40 .29 .50 .52
Sensitivity 93.1% 74.0% 85.1% 91.0% 95.2% 94.0%
Specificity 99.9% 99.2% 99.6% 99.8% 99.8% 99.9%
FDR 2.2% 24.6% 1.7% 4.7% 4.8% .028%
Precision 97.8% 75.4% 98.3% 95.3% 95.2% 97.2%

K-Folds Process

Setup
Values will differ from last submission due to new random sampling of the data for train/test sets.
library(tidyverse)
library(dplyr)
library(caret)
library(class)
library(yardstick)
library(plotly)
library(boot)
library(pROC)
library(glmnet)
library(purrr)
library(gridExtra)
library(randomForest)
library(e1071)
#install.packages("kernlab")
#reading in the data
data <- read.csv("HaitiPixels.csv", header=TRUE ,sep=",")
data <- data %>%
  mutate(BlueClass = as.factor(ifelse(Class=="Blue Tarp","Yes", "No")))
#check the levels just specified
levels(data$BlueClass)
[1] "No"  "Yes"
#set data var to be columns 2-5 of the set
data = data[c(2:5)]
data <- data %>% mutate(id = row_number())
#check addition
head(data$id)
[1] 1 2 3 4 5 6
#shuffle data to fairly split into test / train
shuffleddata = sample_n(data, nrow(data))
#check that it has been shuffled
head(shuffleddata$id) #different then the first six lines of the csv file
[1]  2528  4582 46254 27551 40372 45830
#remove the id column
shuffleddata = shuffleddata[c(1:4)]
#split the data into test and train for use in our upcoming models 
#using a 10k subset for faster knn function execution and file freezing issues
samp <- 1:10000 
samp2 <- 10001:20000
train<-shuffleddata[samp,]
test  <- shuffleddata[samp2,]
head(train)
head(test)
KNN
#model training rules for all models
train_control <- caret::trainControl(method="cv", number=10, returnResamp='all', classProbs=TRUE, savePredictions='final')
#KNN model
system.time({
knnmod=train(BlueClass~Red+Green+Blue,data=train,trControl=train_control,method="knn",preProcess = c("center","scale"), tuneGrid = expand.grid(k = c(1:15)))
})
   user  system elapsed 
 17.430   0.224  17.741 
knnmod
k-Nearest Neighbors 

10000 samples
    3 predictor
    2 classes: 'No', 'Yes' 

Pre-processing: centered (3), scaled (3) 
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 9000, 9001, 9000, 9001, 8999, 9000, ... 
Resampling results across tuning parameters:

  k   Accuracy   Kappa    
   1  0.9958002  0.9300980
   2  0.9961002  0.9350348
   3  0.9961003  0.9359875
   4  0.9964002  0.9408173
   5  0.9963002  0.9389018
   6  0.9962001  0.9375236
   7  0.9962007  0.9380805
   8  0.9959007  0.9325120
   9  0.9959005  0.9314887
  10  0.9960002  0.9334969
  11  0.9954004  0.9234100
  12  0.9955003  0.9242257
  13  0.9954003  0.9228145
  14  0.9953005  0.9208521
  15  0.9954002  0.9223463

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was k = 4.
#plot KNN model
plot(knnmod)

#set prediction, probability, and cv score variables in case needed
knnmod_pred <- predict(knnmod, test,'raw')
knnmod_prob <- predict(knnmod, test,'prob')
knnmod_scored <- cbind(test, knnmod_pred, knnmod_prob)
#AUC/ROC
options(yardstick.event_first=FALSE)
#area under the curve
knn_auc = knnmod_prob %>%
  yardstick::roc_auc(truth=test$BlueClass, Yes)
The `yardstick.event_first` option has been deprecated as of yardstick 0.0.7 and will be completely ignored in a future version.
Instead, set the following argument directly in the metric function:
`options(yardstick.event_first = TRUE)`  -> `event_level = 'first'` (the default)
`options(yardstick.event_first = FALSE)` -> `event_level = 'second'`
This warning is displayed once per session.
knn_auc
#ROC curve + plot
ROC_curve<-knnmod_prob %>%
  yardstick::roc_curve(truth=test$BlueClass,estimate=Yes) %>%
  dplyr::mutate(one_minus_specificity = 1-specificity)
ROC_curve_plot <- ROC_curve %>%
  ggplot(aes(x=one_minus_specificity,y=sensitivity))+
  geom_line() + geom_point() +
  geom_abline(slope = 1,intercept = 0, linetype='dashed',color='blue')+
  xlab("one_minus_specificity\n(false positive rate)")+
  ggtitle('KNN ROC curve')
ggplotly(ROC_curve_plot)
#set threshold
knnmod_pred2 <- knnmod$pred %>% 
  #the accuracy doesn't improve by reducing the threshold any further than .66, 99.7% best. 
  mutate(prediction = ifelse(Yes>.66, 'Yes', 'No')) %>%
  mutate(prediction = factor(prediction, levels=c('No','Yes')))
#confusion matrix
confusionMatrix(knnmod_pred2$prediction, knnmod_pred2$obs, positive="Yes")
Confusion Matrix and Statistics

          Reference
Prediction   No  Yes
       No  9673   26
       Yes   13  288
                                          
               Accuracy : 0.9961          
                 95% CI : (0.9947, 0.9972)
    No Information Rate : 0.9686          
    P-Value [Acc > NIR] : < 2e-16         
                                          
                  Kappa : 0.9346          
                                          
 Mcnemar's Test P-Value : 0.05466         
                                          
            Sensitivity : 0.9172          
            Specificity : 0.9987          
         Pos Pred Value : 0.9568          
         Neg Pred Value : 0.9973          
             Prevalence : 0.0314          
         Detection Rate : 0.0288          
   Detection Prevalence : 0.0301          
      Balanced Accuracy : 0.9579          
                                          
       'Positive' Class : Yes             
                                          
LDA
#LDA model
system.time({
ldamod=train(BlueClass~Red+Green+Blue,data=train,trControl=train_control,method="lda",preProcess = c("center","scale"), family="binomial")
})
   user  system elapsed 
  1.090   0.022   1.123 
ldamod
Linear Discriminant Analysis 

10000 samples
    3 predictor
    2 classes: 'No', 'Yes' 

Pre-processing: centered (3), scaled (3) 
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 9000, 8999, 9001, 9000, 9000, 9000, ... 
Resampling results:

  Accuracy   Kappa    
  0.9850008  0.7661904
#set prediction, probability, and cv score variables in case needed
ldamod_pred <- predict(ldamod, test,'raw')
ldamod_prob <- predict(ldamod, test,'prob')
ldamod_scored <- cbind(test, ldamod_pred, ldamod_prob)
#AUC/ROC
options(yardstick.event_first=FALSE)
#area under the curve
lda_auc = ldamod_prob %>%
  yardstick::roc_auc(truth=test$BlueClass, Yes)
lda_auc
#ROC curve + plot
ROC_curve2<-ldamod_prob %>%
  yardstick::roc_curve(truth=test$BlueClass,estimate=Yes) %>%
  dplyr::mutate(one_minus_specificity = 1-specificity)
ROC_curve_plot2 <- ROC_curve2 %>%
  ggplot(aes(x=one_minus_specificity,y=sensitivity))+
  geom_line() + geom_point() +
  geom_abline(slope = 1,intercept = 0, linetype='dashed',color='blue')+
  xlab("one_minus_specificity\n(false positive rate)")+
  ggtitle('LDA ROC curve')
ggplotly(ROC_curve_plot2)
#set new threshold
ldamod_pred2 <- ldamod$pred %>% 
  #the accuracy doesn't improve by reducing the threshold any further than .85, 98.6% best.
  mutate(prediction = ifelse(Yes>.85, 'Yes', 'No')) %>%
  mutate(prediction = factor(prediction, levels=c('No','Yes')))
#new threshold matrix
confusionMatrix(ldamod_pred2$prediction, ldamod_pred2$obs, positive="Yes")
Confusion Matrix and Statistics

          Reference
Prediction   No  Yes
       No  9625   73
       Yes   61  241
                                          
               Accuracy : 0.9866          
                 95% CI : (0.9841, 0.9888)
    No Information Rate : 0.9686          
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.7756          
                                          
 Mcnemar's Test P-Value : 0.342           
                                          
            Sensitivity : 0.7675          
            Specificity : 0.9937          
         Pos Pred Value : 0.7980          
         Neg Pred Value : 0.9925          
             Prevalence : 0.0314          
         Detection Rate : 0.0241          
   Detection Prevalence : 0.0302          
      Balanced Accuracy : 0.8806          
                                          
       'Positive' Class : Yes             
                                          
QDA
#QDA model
system.time({
qdamod=train(BlueClass~Red+Green+Blue,data=train,trControl=train_control,method="qda",preProcess = c("center","scale"), family="binomial")
})
   user  system elapsed 
  1.044   0.013   1.277 
qdamod
Quadratic Discriminant Analysis 

10000 samples
    3 predictor
    2 classes: 'No', 'Yes' 

Pre-processing: centered (3), scaled (3) 
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 9000, 8999, 9000, 9000, 9001, 9000, ... 
Resampling results:

  Accuracy   Kappa   
  0.9924998  0.871937
##set prediction, probability, and cv score variables in case needed
qdamod_pred <- predict(qdamod, test,'raw')
qdamod_prob <- predict(qdamod, test,'prob')
qdamod_scored <- cbind(test, qdamod_pred, qdamod_prob)
#AUC/ROC
options(yardstick.event_first=FALSE)
#area under the curve
qda_auc = qdamod_prob %>%
  yardstick::roc_auc(truth=test$BlueClass, Yes)
qda_auc
#ROC curve + plot
ROC_curve3<-qdamod_prob %>%
  yardstick::roc_curve(truth=test$BlueClass,estimate=Yes) %>%
  dplyr::mutate(one_minus_specificity = 1-specificity)
ROC_curve_plot3 <- ROC_curve3 %>%
  ggplot(aes(x=one_minus_specificity,y=sensitivity))+
  geom_line() + geom_point() +
  geom_abline(slope = 1,intercept = 0, linetype='dashed',color='blue')+
  xlab("one_minus_specificity\n(false positive rate)")+
  ggtitle('LDA ROC curve')
ggplotly(ROC_curve_plot3)
#set new threshold
qdamod_pred2 <- qdamod$pred %>% 
  #the accuracy doesn't improve by reducing the threshold any further than .40, 99.5% best.
  mutate(prediction = ifelse(Yes>.40, 'Yes', 'No')) %>%
  mutate(prediction = factor(prediction, levels=c('No','Yes')))
#new threshold matrix
confusionMatrix(qdamod_pred2$prediction, qdamod_pred2$obs, positive="Yes")
Confusion Matrix and Statistics

          Reference
Prediction   No  Yes
       No  9641   42
       Yes   45  272
                                         
               Accuracy : 0.9913         
                 95% CI : (0.9893, 0.993)
    No Information Rate : 0.9686         
    P-Value [Acc > NIR] : <2e-16         
                                         
                  Kappa : 0.8576         
                                         
 Mcnemar's Test P-Value : 0.8302         
                                         
            Sensitivity : 0.8662         
            Specificity : 0.9954         
         Pos Pred Value : 0.8580         
         Neg Pred Value : 0.9957         
             Prevalence : 0.0314         
         Detection Rate : 0.0272         
   Detection Prevalence : 0.0317         
      Balanced Accuracy : 0.9308         
                                         
       'Positive' Class : Yes            
                                         
LR
#GLM model
system.time({
glmmod=train(BlueClass~Red+Green+Blue,data=train,trControl=train_control,method="glm",preProcess = c("center","scale"), family="binomial")
})
   user  system elapsed 
  1.633   0.060   1.705 
glmmod
Generalized Linear Model 

10000 samples
    3 predictor
    2 classes: 'No', 'Yes' 

Pre-processing: centered (3), scaled (3) 
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 9000, 9001, 8999, 9000, 9000, 9000, ... 
Resampling results:

  Accuracy   Kappa    
  0.9956001  0.9248834
##et prediction, probability, and cv score variables in case needed
glmmod_pred <- predict(glmmod, test,'raw')
glmmod_prob <- predict(glmmod, test,'prob')
glmmod_scored <- cbind(test, glmmod_pred, glmmod_prob)
#AUC/ROC
options(yardstick.event_first=FALSE)
#area under the curve
qda_auc = glmmod_prob %>%
  yardstick::roc_auc(truth=test$BlueClass, Yes)
qda_auc
#ROC curve + plot
ROC_curve4<-glmmod_prob %>%
  yardstick::roc_curve(truth=test$BlueClass,estimate=Yes) %>%
  dplyr::mutate(one_minus_specificity = 1-specificity)
ROC_curve_plot4 <- ROC_curve4 %>%
  ggplot(aes(x=one_minus_specificity,y=sensitivity))+
  geom_line() + geom_point() +
  geom_abline(slope = 1,intercept = 0, linetype='dashed',color='blue')+
  xlab("one_minus_specificity\n(false positive rate)")+
  ggtitle('LDA ROC curve')
ggplotly(ROC_curve_plot4)
#set new threshold
glmmod_pred2 <- glmmod$pred %>% 
  #the accuracy doesn't improve by reducing the threshold any further than .29, 99.6% best.
  mutate(prediction = ifelse(Yes>.29, 'Yes', 'No')) %>%
  mutate(prediction = factor(prediction, levels=c('No','Yes')))
#new threshold matrix
confusionMatrix(glmmod_pred2$prediction, glmmod_pred2$obs, positive="Yes")
Confusion Matrix and Statistics

          Reference
Prediction   No  Yes
       No  9669   23
       Yes   17  291
                                          
               Accuracy : 0.996           
                 95% CI : (0.9946, 0.9971)
    No Information Rate : 0.9686          
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.9336          
                                          
 Mcnemar's Test P-Value : 0.4292          
                                          
            Sensitivity : 0.9268          
            Specificity : 0.9982          
         Pos Pred Value : 0.9448          
         Neg Pred Value : 0.9976          
             Prevalence : 0.0314          
         Detection Rate : 0.0291          
   Detection Prevalence : 0.0308          
      Balanced Accuracy : 0.9625          
                                          
       'Positive' Class : Yes             
                                          
RF
#Random Forest model
#Choosing tuning parameters:
#https://discuss.analyticsvidhya.com/t/how-to-decide-no-of-ntrees-in-randomforest/6882/3
#https://rpubs.com/phamdinhkhanh/389752
#Create control function for training with 10 folds and keep 3 folds for training.
train_control <- caret::trainControl(method="cv", number=10, returnResamp='all', classProbs=TRUE, savePredictions='final')
#https://stackoverflow.com/questions/10085806/extracting-specific-columns-from-a-data-frame
df<- train %>%
  select(Red,Green,Blue)
#mtryStart defaults at sqrt(p)
#my available threshold for mtry values is pretty low based on the size of my dataset 
(tuneRF(df,train$BlueClass,mtry = 5, ntree = 500, stepFactor=5, improve=0.05,
       trace=TRUE, plot=TRUE, doBest=TRUE))
invalid mtry: reset to within valid range
mtry = 5  OOB error = 0.4% 
Searching left ...
mtry = 1    OOB error = 0.41% 
-0.025 0.05 
Searching right ...
mtry = 3    OOB error = 0.39% 
0.025 0.05 

Call:
 randomForest(x = x, y = y, mtry = res[which.min(res[, 2]), 1]) 
               Type of random forest: classification
                     Number of trees: 500
No. of variables tried at each split: 3

        OOB estimate of  error rate: 0.41%
Confusion matrix:
      No Yes class.error
No  9667  19 0.001961594
Yes   22 292 0.070063694

#mtry = 3
##------
tunegrid <- expand.grid(.mtry = 3)
modellist <- list()
#train with different ntree parameters and inspect bias/variance tradeoff 
#findtrees1 <- train(BlueClass~Red+Green+Blue, 
 #              data=train,
  #             method = 'rf',
   #            metric = 'Accuracy',
    #           tuneGrid = tunegrid,
     #          trControl = control,
      #         ntree = 50)
#findtrees1
#findtrees2 <- train(BlueClass~Red+Green+Blue, 
 #              data=train,
  #             method = 'rf',
   #            metric = 'Accuracy',
    #           tuneGrid = tunegrid,
     #          trControl = control,
      #         ntree = 100)
#findtrees2
system.time({
RF <- train(BlueClass~Red+Green+Blue, 
               data=train,
               method = 'rf',
               metric = 'Accuracy',
               tuneGrid = tunegrid,
               trControl = train_control,
               ntree = 500)
})
   user  system elapsed 
 16.611   1.681  18.652 
RF
Random Forest 

10000 samples
    3 predictor
    2 classes: 'No', 'Yes' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 9001, 9000, 8999, 9001, 9000, 8999, ... 
Resampling results:

  Accuracy   Kappa    
  0.9959998  0.9337306

Tuning parameter 'mtry' was held constant at a value of 3
##et prediction, probability, and cv score variables in case needed
rfmod_pred <- predict(RF, test,'raw')
rfmod_prob <- predict(RF, test,'prob')
rfmod_scored <- cbind(test, rfmod_pred, rfmod_prob)
#AUC/ROC
options(yardstick.event_first=FALSE)
#area under the curve
rf_auc = rfmod_prob %>%
  yardstick::roc_auc(truth=test$BlueClass, Yes)
rf_auc
#ROC curve + plot
ROC_curve4<-rfmod_prob %>%
  yardstick::roc_curve(truth=test$BlueClass,estimate=Yes) %>%
  dplyr::mutate(one_minus_specificity = 1-specificity)
ROC_curve_plot4 <- ROC_curve4 %>%
  ggplot(aes(x=one_minus_specificity,y=sensitivity))+
  geom_line() + geom_point() +
  geom_abline(slope = 1,intercept = 0, linetype='dashed',color='blue')+
  xlab("one_minus_specificity\n(false positive rate)")+
  ggtitle('LDA ROC curve')
ggplotly(ROC_curve_plot4)
#set new threshold
rfmod_pred2 <- RF$pred %>% 
  #the accuracy doesn't improve by reducing the threshold any further than .50, 99.7% best.
  mutate(prediction = ifelse(Yes>.50, 'Yes', 'No')) %>%
  mutate(prediction = factor(prediction, levels=c('No','Yes')))
#new threshold matrix
confusionMatrix(rfmod_pred2$prediction, rfmod_pred2$obs, positive="Yes")
Confusion Matrix and Statistics

          Reference
Prediction   No  Yes
       No  9669   23
       Yes   17  291
                                          
               Accuracy : 0.996           
                 95% CI : (0.9946, 0.9971)
    No Information Rate : 0.9686          
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.9336          
                                          
 Mcnemar's Test P-Value : 0.4292          
                                          
            Sensitivity : 0.9268          
            Specificity : 0.9982          
         Pos Pred Value : 0.9448          
         Neg Pred Value : 0.9976          
             Prevalence : 0.0314          
         Detection Rate : 0.0291          
   Detection Prevalence : 0.0308          
      Balanced Accuracy : 0.9625          
                                          
       'Positive' Class : Yes             
                                          
SVM
#Choosing tuning parameters:
#Linear
set.seed(1)
tune.out.linear=tune(svm,BlueClass~Red+Green+Blue,data=train,kernel="linear",ranges=list(cost=c(0.001, 0.01, 0.1, 1,5,10,100)))

#Radial
tune.out.radial=tune(svm, BlueClass~Red+Green+Blue,data=train, kernel="radial", ranges=list(cost=c(0.1,1,10,100,1000),gamma=c(0.5,1,2,3,4)))
#lowest error is radial cost 10 gamma 1

#Poly
tune.out.poly=tune(svm,BlueClass~Red+Green+Blue,data=train, kernel="polynomial", ranges=list(cost=c(0.1,1,10,100,1000),degree=c(1,2,3,4,5)))

##------

system.time({
svmmod <- train(BlueClass~Red+Green+Blue, 
               data=train,
               method = 'svmRadial',
               metric = 'Accuracy',
               trControl = train_control,
               cost = 10,
               gamma = 1,
               preProcess = c("center","scale")
               )
})
#Set prediction, probability, and cv score variables in case needed
svmmod_pred <- predict(svmmod, test,'raw')
svmmod_prob <- predict(svmmod, test,'prob')
svmmod_scored <- cbind(test, svmmod_pred, svmmod_prob)
#AUC/ROC
options(yardstick.event_first=FALSE)
#area under the curve
svmmod_auc = svmmod_prob %>%
  yardstick::roc_auc(truth=test$BlueClass, Yes)
svmmod_auc
#ROC curve + plot
ROC_curve4<-svmmod_prob %>%
  yardstick::roc_curve(truth=test$BlueClass,estimate=Yes) %>%
  dplyr::mutate(one_minus_specificity = 1-specificity)
ROC_curve_plot4 <- ROC_curve4 %>%
  ggplot(aes(x=one_minus_specificity,y=sensitivity))+
  geom_line() + geom_point() +
  geom_abline(slope = 1,intercept = 0, linetype='dashed',color='blue')+
  xlab("one_minus_specificity\n(false positive rate)")+
  ggtitle('LDA ROC curve')
ggplotly(ROC_curve_plot4)
#set new threshold
svmmod_pred2 <- svmmod$pred %>% 
  #the accuracy doesn't improve by reducing the threshold any further than .52, 99.7% best.
  mutate(prediction = ifelse(Yes>.52, 'Yes', 'No')) %>%
  mutate(prediction = factor(prediction, levels=c('No','Yes')))
#new threshold matrix
confusionMatrix(svmmod_pred2$prediction, svmmod_pred2$obs, positive="Yes")
Confusion Matrix and Statistics

          Reference
Prediction   No  Yes
       No  9673   28
       Yes   13  286
                                          
               Accuracy : 0.9959          
                 95% CI : (0.9944, 0.9971)
    No Information Rate : 0.9686          
    P-Value [Acc > NIR] : < 2e-16         
                                          
                  Kappa : 0.931           
                                          
 Mcnemar's Test P-Value : 0.02878         
                                          
            Sensitivity : 0.9108          
            Specificity : 0.9987          
         Pos Pred Value : 0.9565          
         Neg Pred Value : 0.9971          
             Prevalence : 0.0314          
         Detection Rate : 0.0286          
   Detection Prevalence : 0.0299          
      Balanced Accuracy : 0.9547          
                                          
       'Positive' Class : Yes             
                                          
Tuning parameter selection process
  1. Interpretation of your chosen tuning parameter values:
  • Ntree = 500 indicates that 500 trees were created. I understand that it is important for this number to be substantial to reduce variance, but as we know, that can also lead to bias. There should be a sweet spot where every input row gets predicted at least a few times without overfitting the trees. Mtry=5 indicates that 5 variables were randomly sampled at each split. When mtry=p it can essentially equate to bagging, whereas if its set to 1 it essentially chooses a random variable. I understand it is good to try out a few values that range no smaller than 2 and no larger than p. The gamma=1 svm parameter indicates that a single training example has far reaching influence. A cost = 10 here means we are "paying" a high price for higher accuracy.
  1. Explanation of how your chosen tuning parameter values were chosen:
  • I tried to make this selection process as programmatic as possible. There was a lot of learning and I'm sure there is still a healthy amount that should be corrected within the code but I was glad to find the resources that I did. For mtry I used the tuneRF() function that accepts an initial value for mtry and returns the out-of-bag error for your input value as well as a few surrounding values. I chose mtry because it produced the lowest oob error and went on to choose ntrees from there. At that point I included my mtry=3 into several RF models with ntrees of different values and again looked for the highest accuracy. Ntrees=500 was the winner in that sense, and it appears to be a very commonly used value for that parameter. For the SVM parameters I used our class lab as a guide to run the tune() function for linear, radial and polynomial kernels with several cost, gamma and degree values respectively. The tune function returns a best performer, which was the radial kernel with cost = 10 and gamma = 1. When running the train function the sigma = 8.691262 and C = 1 values were returned as contributing to the highest accuracy and therefore were the most optimal values for those parameters.
Conclusions
  1. A discussion of the best performing algorithm(s) in the cross-validation and hold-out data
  • The best performing algorithm in the cross validation spectrum would be GLM or QDA. Both run in under 1.5 seconds to the user (up to ~15 seconds faster than some of the others) and still have an accuracy over 99.4%. The hold out data general ran very slowly, which could be due to the size of the dataset, but given the consistently high accuracy and easy of use of these two functions I would be even more likely to advocate for them in hold out setting.
  1. A recommendation and rationale regarding which algorithm to use for detection of blue tarps
  • For the detection of blue tarps I’d recommend using the radial kernel svm function. Its valuable to tune your parameters according to your data and how you may define accuracy. Particularly with this pixel data, you could lower the threshold for determining a blue tarp knowing that it goes beyond the original tarp limits, but avoids the possibility of neglecting a human in need. The reason I became partial to these functions in the context of a natural disaster, is because it can give you a starting point for additional tuning. Accuracy and automation are two things I would assume are extremely valuable in the wake of a natural disaster.
  1. Additional thoughts and questions:
  • I’ve done some research to learn more about how RGB average thresholds are set in order to determine that it is a “green” vegetation area or “red” soil area. I understand from a paper titled “Geospatial Disaster Response during the Haiti Earthquake: A Case Study Spanning Airborne Deployment, Data Collection, Transfer, Processing, and Dissemination” that a Mahalanobis distance classifier was trained using a collection of known blue tarp pixels, the RGB values of the pixels in the photos were averaged, and that’s how the other pixels are determined to be blue tarp or not to be blue tarp. My issue is largely that the average of those three values is not exclusive to the color. I read that “RGB (Red, Green, Blue) are 8 bit each. The range for each individual colour is 0-255 (as 2^8 = 256 possibilities). The combination range is 256256256. By dividing by 255, the 0-255 range can be described with a 0.0-1.0 range where 0.0 means 0 (0x00) and 1.0 means 255 (0xFF).” So how could the average of these three values indicate something is blue over it being red, when the average for something being ‘true’ red (255,0,0) would have the same average as something being ‘true’ green or blue at (0,255,0) or (0,0,255) respectively. It seems more logical for there to be a lower bound limit to what could be considered blue, in addition to it being at least 1.2x the values of the other two for example. There are studies around accessibility and exactly what shade behins to be unseeable by someone who is color blind, how do they determine their thresholds? That will be my next rabbit hole.
LS0tCmF1dGhvcjogIkFsZXhhbmRyYSBDYXRoY2FydCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgojIERpc2FzdGVyIFJlbGllZiBQcm9qZWN0IHsudGFic2V0fQoKIyMjICBLLUZvbGRzIE91dCBvZiBTYW1wbGluZyBQZXJmb3JtYW5jZSAoICsgUkYvU1ZNICkKCk1ldGhvZCAgICAgIEtOTihrPTUpICBMREEgICAgICBRREEgICAgICBMUiAgICAgICBSRih0dW5pbmcgcGFyYW1ldGVyOiBtdHJ5PTM7IG50cmVlcz01MDApICBTVk0odHVuaW5nIHBhcmFtZXRlcjogY29zdD0xMDsgZ2FtbWE9MTsgc2lnbWEgPSA4LjY5MTI2MjsgQyA9IDEpCi0tLS0tLS0tLS0tIC0tLS0tLS0tLSAtLS0tLS0tLSAtLS0tLS0tLSAtLS0tLS0tLSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogICBBY2N1cmFjeSA5OS42JSAgICAgOTguMyUgICAgOTkuNCUgICAgOTkuNSUgICAgOTkuNyUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgOTkuNyUgCiAgICAgICAgQVVDIDk5LjglICAgICA5OC43JSAgICA5OS44JSAgICA5OS44JSAgICA5OS4wJSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA5OS40JQogICAgICAgIFJPQyBpbiB0YWJzICAgaW4gdGFicyAgaW4gdGFicyAgaW4gdGFicyAgaW4gdGFicyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW4gdGFicwogIFRocmVzaG9sZCAuNjYgICAgICAgLjg1ICAgICAgLjQwICAgICAgLjI5ICAgICAgLjUwICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLjUyClNlbnNpdGl2aXR5IDkzLjElICAgICA3NC4wJSAgICA4NS4xJSAgICA5MS4wJSAgICA5NS4yJSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA5NC4wJQpTcGVjaWZpY2l0eSA5OS45JSAgICAgOTkuMiUgICAgOTkuNiUgICAgOTkuOCUgICAgOTkuOCUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgOTkuOSUKICAgICAgICBGRFIgMi4yJSAgICAgIDI0LjYlICAgIDEuNyUgICAgIDQuNyUgICAgIDQuOCUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4wMjglCiAgUHJlY2lzaW9uIDk3LjglICAgICA3NS40JSAgICA5OC4zJSAgICA5NS4zJSAgICA5NS4yJSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA5Ny4yJQoKCiMjIyMgSy1Gb2xkcyBQcm9jZXNzIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQojIyMjIyBTZXR1cCAKIyMjIyMjIFZhbHVlcyB3aWxsIGRpZmZlciBmcm9tIGxhc3Qgc3VibWlzc2lvbiBkdWUgdG8gbmV3IHJhbmRvbSBzYW1wbGluZyBvZiB0aGUgZGF0YSBmb3IgdHJhaW4vdGVzdCBzZXRzLgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoY2xhc3MpCmxpYnJhcnkoeWFyZHN0aWNrKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShib290KQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkoZ2xtbmV0KQpsaWJyYXJ5KHB1cnJyKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoZTEwNzEpCmluc3RhbGwucGFja2FnZXMoImtlcm5sYWIiKQpgYGAKCmBgYHtyfQojcmVhZGluZyBpbiB0aGUgZGF0YQpkYXRhIDwtIHJlYWQuY3N2KCJIYWl0aVBpeGVscy5jc3YiLCBoZWFkZXI9VFJVRSAsc2VwPSIsIikKCmRhdGEgPC0gZGF0YSAlPiUKICBtdXRhdGUoQmx1ZUNsYXNzID0gYXMuZmFjdG9yKGlmZWxzZShDbGFzcz09IkJsdWUgVGFycCIsIlllcyIsICJObyIpKSkKYGBgCgpgYGB7cn0KI2NoZWNrIHRoZSBsZXZlbHMganVzdCBzcGVjaWZpZWQKbGV2ZWxzKGRhdGEkQmx1ZUNsYXNzKQojc2V0IGRhdGEgdmFyIHRvIGJlIGNvbHVtbnMgMi01IG9mIHRoZSBzZXQKZGF0YSA9IGRhdGFbYygyOjUpXQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmRhdGEgPC0gZGF0YSAlPiUgbXV0YXRlKGlkID0gcm93X251bWJlcigpKQojY2hlY2sgYWRkaXRpb24KaGVhZChkYXRhJGlkKQojc2h1ZmZsZSBkYXRhIHRvIGZhaXJseSBzcGxpdCBpbnRvIHRlc3QgLyB0cmFpbgpzaHVmZmxlZGRhdGEgPSBzYW1wbGVfbihkYXRhLCBucm93KGRhdGEpKQojY2hlY2sgdGhhdCBpdCBoYXMgYmVlbiBzaHVmZmxlZApoZWFkKHNodWZmbGVkZGF0YSRpZCkgI2RpZmZlcmVudCB0aGVuIHRoZSBmaXJzdCBzaXggbGluZXMgb2YgdGhlIGNzdiBmaWxlCiNyZW1vdmUgdGhlIGlkIGNvbHVtbgpzaHVmZmxlZGRhdGEgPSBzaHVmZmxlZGRhdGFbYygxOjQpXQpgYGAKCmBgYHtyfQojc3BsaXQgdGhlIGRhdGEgaW50byB0ZXN0IGFuZCB0cmFpbiBmb3IgdXNlIGluIG91ciB1cGNvbWluZyBtb2RlbHMgCiN1c2luZyBhIDEwayBzdWJzZXQgZm9yIGZhc3RlciBrbm4gZnVuY3Rpb24gZXhlY3V0aW9uIGFuZCBmaWxlIGZyZWV6aW5nIGlzc3VlcwpzYW1wIDwtIDE6MTAwMDAgCnNhbXAyIDwtIDEwMDAxOjIwMDAwCnRyYWluPC1zaHVmZmxlZGRhdGFbc2FtcCxdCnRlc3QgIDwtIHNodWZmbGVkZGF0YVtzYW1wMixdCmhlYWQodHJhaW4pCmhlYWQodGVzdCkKYGBgCgojIyMjIyBLTk4KCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiNtb2RlbCB0cmFpbmluZyBydWxlcyBmb3IgYWxsIG1vZGVscwp0cmFpbl9jb250cm9sIDwtIGNhcmV0Ojp0cmFpbkNvbnRyb2wobWV0aG9kPSJjdiIsIG51bWJlcj0xMCwgcmV0dXJuUmVzYW1wPSdhbGwnLCBjbGFzc1Byb2JzPVRSVUUsIHNhdmVQcmVkaWN0aW9ucz0nZmluYWwnKQoKI0tOTiBtb2RlbApzeXN0ZW0udGltZSh7Cmtubm1vZD10cmFpbihCbHVlQ2xhc3N+UmVkK0dyZWVuK0JsdWUsZGF0YT10cmFpbix0ckNvbnRyb2w9dHJhaW5fY29udHJvbCxtZXRob2Q9ImtubiIscHJlUHJvY2VzcyA9IGMoImNlbnRlciIsInNjYWxlIiksIHR1bmVHcmlkID0gZXhwYW5kLmdyaWQoayA9IGMoMToxNSkpKQp9KQprbm5tb2QKYGBgCgpgYGB7cn0KI3Bsb3QgS05OIG1vZGVsCnBsb3Qoa25ubW9kKQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiNzZXQgcHJlZGljdGlvbiwgcHJvYmFiaWxpdHksIGFuZCBjdiBzY29yZSB2YXJpYWJsZXMgaW4gY2FzZSBuZWVkZWQKa25ubW9kX3ByZWQgPC0gcHJlZGljdChrbm5tb2QsIHRlc3QsJ3JhdycpCmtubm1vZF9wcm9iIDwtIHByZWRpY3Qoa25ubW9kLCB0ZXN0LCdwcm9iJykKCmtubm1vZF9zY29yZWQgPC0gY2JpbmQodGVzdCwga25ubW9kX3ByZWQsIGtubm1vZF9wcm9iKQoKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojQVVDL1JPQwpvcHRpb25zKHlhcmRzdGljay5ldmVudF9maXJzdD1GQUxTRSkKCiNhcmVhIHVuZGVyIHRoZSBjdXJ2ZQprbm5fYXVjID0ga25ubW9kX3Byb2IgJT4lCiAgeWFyZHN0aWNrOjpyb2NfYXVjKHRydXRoPXRlc3QkQmx1ZUNsYXNzLCBZZXMpCmtubl9hdWMKCiNST0MgY3VydmUgKyBwbG90ClJPQ19jdXJ2ZTwta25ubW9kX3Byb2IgJT4lCiAgeWFyZHN0aWNrOjpyb2NfY3VydmUodHJ1dGg9dGVzdCRCbHVlQ2xhc3MsZXN0aW1hdGU9WWVzKSAlPiUKICBkcGx5cjo6bXV0YXRlKG9uZV9taW51c19zcGVjaWZpY2l0eSA9IDEtc3BlY2lmaWNpdHkpCgpST0NfY3VydmVfcGxvdCA8LSBST0NfY3VydmUgJT4lCiAgZ2dwbG90KGFlcyh4PW9uZV9taW51c19zcGVjaWZpY2l0eSx5PXNlbnNpdGl2aXR5KSkrCiAgZ2VvbV9saW5lKCkgKyBnZW9tX3BvaW50KCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSxpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZT0nZGFzaGVkJyxjb2xvcj0nYmx1ZScpKwogIHhsYWIoIm9uZV9taW51c19zcGVjaWZpY2l0eVxuKGZhbHNlIHBvc2l0aXZlIHJhdGUpIikrCiAgZ2d0aXRsZSgnS05OIFJPQyBjdXJ2ZScpCmdncGxvdGx5KFJPQ19jdXJ2ZV9wbG90KQpgYGAKCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojc2V0IHRocmVzaG9sZAprbm5tb2RfcHJlZDIgPC0ga25ubW9kJHByZWQgJT4lIAogICN0aGUgYWNjdXJhY3kgZG9lc24ndCBpbXByb3ZlIGJ5IHJlZHVjaW5nIHRoZSB0aHJlc2hvbGQgYW55IGZ1cnRoZXIgdGhhbiAuNjYsIDk5LjclIGJlc3QuIAogIG11dGF0ZShwcmVkaWN0aW9uID0gaWZlbHNlKFllcz4uNjYsICdZZXMnLCAnTm8nKSkgJT4lCiAgbXV0YXRlKHByZWRpY3Rpb24gPSBmYWN0b3IocHJlZGljdGlvbiwgbGV2ZWxzPWMoJ05vJywnWWVzJykpKQoKI2NvbmZ1c2lvbiBtYXRyaXgKY29uZnVzaW9uTWF0cml4KGtubm1vZF9wcmVkMiRwcmVkaWN0aW9uLCBrbm5tb2RfcHJlZDIkb2JzLCBwb3NpdGl2ZT0iWWVzIikKCmBgYAoKIyMjIyMgTERBCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQojTERBIG1vZGVsCnN5c3RlbS50aW1lKHsKbGRhbW9kPXRyYWluKEJsdWVDbGFzc35SZWQrR3JlZW4rQmx1ZSxkYXRhPXRyYWluLHRyQ29udHJvbD10cmFpbl9jb250cm9sLG1ldGhvZD0ibGRhIixwcmVQcm9jZXNzID0gYygiY2VudGVyIiwic2NhbGUiKSwgZmFtaWx5PSJiaW5vbWlhbCIpCn0pCmxkYW1vZApgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiNzZXQgcHJlZGljdGlvbiwgcHJvYmFiaWxpdHksIGFuZCBjdiBzY29yZSB2YXJpYWJsZXMgaW4gY2FzZSBuZWVkZWQKbGRhbW9kX3ByZWQgPC0gcHJlZGljdChsZGFtb2QsIHRlc3QsJ3JhdycpCmxkYW1vZF9wcm9iIDwtIHByZWRpY3QobGRhbW9kLCB0ZXN0LCdwcm9iJykKCmxkYW1vZF9zY29yZWQgPC0gY2JpbmQodGVzdCwgbGRhbW9kX3ByZWQsIGxkYW1vZF9wcm9iKQpgYGAKCmBgYHtyfQojQVVDL1JPQwpvcHRpb25zKHlhcmRzdGljay5ldmVudF9maXJzdD1GQUxTRSkKCiNhcmVhIHVuZGVyIHRoZSBjdXJ2ZQpsZGFfYXVjID0gbGRhbW9kX3Byb2IgJT4lCiAgeWFyZHN0aWNrOjpyb2NfYXVjKHRydXRoPXRlc3QkQmx1ZUNsYXNzLCBZZXMpCmxkYV9hdWMKCiNST0MgY3VydmUgKyBwbG90ClJPQ19jdXJ2ZTI8LWxkYW1vZF9wcm9iICU+JQogIHlhcmRzdGljazo6cm9jX2N1cnZlKHRydXRoPXRlc3QkQmx1ZUNsYXNzLGVzdGltYXRlPVllcykgJT4lCiAgZHBseXI6Om11dGF0ZShvbmVfbWludXNfc3BlY2lmaWNpdHkgPSAxLXNwZWNpZmljaXR5KQoKUk9DX2N1cnZlX3Bsb3QyIDwtIFJPQ19jdXJ2ZTIgJT4lCiAgZ2dwbG90KGFlcyh4PW9uZV9taW51c19zcGVjaWZpY2l0eSx5PXNlbnNpdGl2aXR5KSkrCiAgZ2VvbV9saW5lKCkgKyBnZW9tX3BvaW50KCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSxpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZT0nZGFzaGVkJyxjb2xvcj0nYmx1ZScpKwogIHhsYWIoIm9uZV9taW51c19zcGVjaWZpY2l0eVxuKGZhbHNlIHBvc2l0aXZlIHJhdGUpIikrCiAgZ2d0aXRsZSgnTERBIFJPQyBjdXJ2ZScpCmdncGxvdGx5KFJPQ19jdXJ2ZV9wbG90MikKYGBgCgpgYGB7cn0KI3NldCBuZXcgdGhyZXNob2xkCmxkYW1vZF9wcmVkMiA8LSBsZGFtb2QkcHJlZCAlPiUgCiAgI3RoZSBhY2N1cmFjeSBkb2Vzbid0IGltcHJvdmUgYnkgcmVkdWNpbmcgdGhlIHRocmVzaG9sZCBhbnkgZnVydGhlciB0aGFuIC44NSwgOTguNiUgYmVzdC4KICBtdXRhdGUocHJlZGljdGlvbiA9IGlmZWxzZShZZXM+Ljg1LCAnWWVzJywgJ05vJykpICU+JQogIG11dGF0ZShwcmVkaWN0aW9uID0gZmFjdG9yKHByZWRpY3Rpb24sIGxldmVscz1jKCdObycsJ1llcycpKSkKCiNuZXcgdGhyZXNob2xkIG1hdHJpeApjb25mdXNpb25NYXRyaXgobGRhbW9kX3ByZWQyJHByZWRpY3Rpb24sIGxkYW1vZF9wcmVkMiRvYnMsIHBvc2l0aXZlPSJZZXMiKQpgYGAKCiMjIyMjIFFEQQoKYGBge3Igd2FybmluZz1GQUxTRX0KI1FEQSBtb2RlbApzeXN0ZW0udGltZSh7CnFkYW1vZD10cmFpbihCbHVlQ2xhc3N+UmVkK0dyZWVuK0JsdWUsZGF0YT10cmFpbix0ckNvbnRyb2w9dHJhaW5fY29udHJvbCxtZXRob2Q9InFkYSIscHJlUHJvY2VzcyA9IGMoImNlbnRlciIsInNjYWxlIiksIGZhbWlseT0iYmlub21pYWwiKQp9KQpxZGFtb2QKYGBgCgpgYGB7cn0KIyNzZXQgcHJlZGljdGlvbiwgcHJvYmFiaWxpdHksIGFuZCBjdiBzY29yZSB2YXJpYWJsZXMgaW4gY2FzZSBuZWVkZWQKcWRhbW9kX3ByZWQgPC0gcHJlZGljdChxZGFtb2QsIHRlc3QsJ3JhdycpCnFkYW1vZF9wcm9iIDwtIHByZWRpY3QocWRhbW9kLCB0ZXN0LCdwcm9iJykKCnFkYW1vZF9zY29yZWQgPC0gY2JpbmQodGVzdCwgcWRhbW9kX3ByZWQsIHFkYW1vZF9wcm9iKQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiNBVUMvUk9DCm9wdGlvbnMoeWFyZHN0aWNrLmV2ZW50X2ZpcnN0PUZBTFNFKQoKI2FyZWEgdW5kZXIgdGhlIGN1cnZlCnFkYV9hdWMgPSBxZGFtb2RfcHJvYiAlPiUKICB5YXJkc3RpY2s6OnJvY19hdWModHJ1dGg9dGVzdCRCbHVlQ2xhc3MsIFllcykKcWRhX2F1YwoKI1JPQyBjdXJ2ZSArIHBsb3QKUk9DX2N1cnZlMzwtcWRhbW9kX3Byb2IgJT4lCiAgeWFyZHN0aWNrOjpyb2NfY3VydmUodHJ1dGg9dGVzdCRCbHVlQ2xhc3MsZXN0aW1hdGU9WWVzKSAlPiUKICBkcGx5cjo6bXV0YXRlKG9uZV9taW51c19zcGVjaWZpY2l0eSA9IDEtc3BlY2lmaWNpdHkpCgpST0NfY3VydmVfcGxvdDMgPC0gUk9DX2N1cnZlMyAlPiUKICBnZ3Bsb3QoYWVzKHg9b25lX21pbnVzX3NwZWNpZmljaXR5LHk9c2Vuc2l0aXZpdHkpKSsKICBnZW9tX2xpbmUoKSArIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLGludGVyY2VwdCA9IDAsIGxpbmV0eXBlPSdkYXNoZWQnLGNvbG9yPSdibHVlJykrCiAgeGxhYigib25lX21pbnVzX3NwZWNpZmljaXR5XG4oZmFsc2UgcG9zaXRpdmUgcmF0ZSkiKSsKICBnZ3RpdGxlKCdMREEgUk9DIGN1cnZlJykKZ2dwbG90bHkoUk9DX2N1cnZlX3Bsb3QzKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiNzZXQgbmV3IHRocmVzaG9sZApxZGFtb2RfcHJlZDIgPC0gcWRhbW9kJHByZWQgJT4lIAogICN0aGUgYWNjdXJhY3kgZG9lc24ndCBpbXByb3ZlIGJ5IHJlZHVjaW5nIHRoZSB0aHJlc2hvbGQgYW55IGZ1cnRoZXIgdGhhbiAuNDAsIDk5LjUlIGJlc3QuCiAgbXV0YXRlKHByZWRpY3Rpb24gPSBpZmVsc2UoWWVzPi40MCwgJ1llcycsICdObycpKSAlPiUKICBtdXRhdGUocHJlZGljdGlvbiA9IGZhY3RvcihwcmVkaWN0aW9uLCBsZXZlbHM9YygnTm8nLCdZZXMnKSkpCgojbmV3IHRocmVzaG9sZCBtYXRyaXgKY29uZnVzaW9uTWF0cml4KHFkYW1vZF9wcmVkMiRwcmVkaWN0aW9uLCBxZGFtb2RfcHJlZDIkb2JzLCBwb3NpdGl2ZT0iWWVzIikKYGBgCgojIyMjIyBMUgoKYGBge3Igd2FybmluZz1GQUxTRX0KI0dMTSBtb2RlbApzeXN0ZW0udGltZSh7CmdsbW1vZD10cmFpbihCbHVlQ2xhc3N+UmVkK0dyZWVuK0JsdWUsZGF0YT10cmFpbix0ckNvbnRyb2w9dHJhaW5fY29udHJvbCxtZXRob2Q9ImdsbSIscHJlUHJvY2VzcyA9IGMoImNlbnRlciIsInNjYWxlIiksIGZhbWlseT0iYmlub21pYWwiKQp9KQpnbG1tb2QKYGBgCiAKYGBge3J9CiMjZXQgcHJlZGljdGlvbiwgcHJvYmFiaWxpdHksIGFuZCBjdiBzY29yZSB2YXJpYWJsZXMgaW4gY2FzZSBuZWVkZWQKZ2xtbW9kX3ByZWQgPC0gcHJlZGljdChnbG1tb2QsIHRlc3QsJ3JhdycpCmdsbW1vZF9wcm9iIDwtIHByZWRpY3QoZ2xtbW9kLCB0ZXN0LCdwcm9iJykKCmdsbW1vZF9zY29yZWQgPC0gY2JpbmQodGVzdCwgZ2xtbW9kX3ByZWQsIGdsbW1vZF9wcm9iKQpgYGAKCmBgYHtyfQojQVVDL1JPQwpvcHRpb25zKHlhcmRzdGljay5ldmVudF9maXJzdD1GQUxTRSkKCiNhcmVhIHVuZGVyIHRoZSBjdXJ2ZQpxZGFfYXVjID0gZ2xtbW9kX3Byb2IgJT4lCiAgeWFyZHN0aWNrOjpyb2NfYXVjKHRydXRoPXRlc3QkQmx1ZUNsYXNzLCBZZXMpCnFkYV9hdWMKCiNST0MgY3VydmUgKyBwbG90ClJPQ19jdXJ2ZTQ8LWdsbW1vZF9wcm9iICU+JQogIHlhcmRzdGljazo6cm9jX2N1cnZlKHRydXRoPXRlc3QkQmx1ZUNsYXNzLGVzdGltYXRlPVllcykgJT4lCiAgZHBseXI6Om11dGF0ZShvbmVfbWludXNfc3BlY2lmaWNpdHkgPSAxLXNwZWNpZmljaXR5KQoKUk9DX2N1cnZlX3Bsb3Q0IDwtIFJPQ19jdXJ2ZTQgJT4lCiAgZ2dwbG90KGFlcyh4PW9uZV9taW51c19zcGVjaWZpY2l0eSx5PXNlbnNpdGl2aXR5KSkrCiAgZ2VvbV9saW5lKCkgKyBnZW9tX3BvaW50KCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSxpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZT0nZGFzaGVkJyxjb2xvcj0nYmx1ZScpKwogIHhsYWIoIm9uZV9taW51c19zcGVjaWZpY2l0eVxuKGZhbHNlIHBvc2l0aXZlIHJhdGUpIikrCiAgZ2d0aXRsZSgnTERBIFJPQyBjdXJ2ZScpCmdncGxvdGx5KFJPQ19jdXJ2ZV9wbG90NCkKYGBgCgoKYGBge3J9CiNzZXQgbmV3IHRocmVzaG9sZApnbG1tb2RfcHJlZDIgPC0gZ2xtbW9kJHByZWQgJT4lIAogICN0aGUgYWNjdXJhY3kgZG9lc24ndCBpbXByb3ZlIGJ5IHJlZHVjaW5nIHRoZSB0aHJlc2hvbGQgYW55IGZ1cnRoZXIgdGhhbiAuMjksIDk5LjYlIGJlc3QuCiAgbXV0YXRlKHByZWRpY3Rpb24gPSBpZmVsc2UoWWVzPi4yOSwgJ1llcycsICdObycpKSAlPiUKICBtdXRhdGUocHJlZGljdGlvbiA9IGZhY3RvcihwcmVkaWN0aW9uLCBsZXZlbHM9YygnTm8nLCdZZXMnKSkpCgojbmV3IHRocmVzaG9sZCBtYXRyaXgKY29uZnVzaW9uTWF0cml4KGdsbW1vZF9wcmVkMiRwcmVkaWN0aW9uLCBnbG1tb2RfcHJlZDIkb2JzLCBwb3NpdGl2ZT0iWWVzIikKYGBgCgojIyMjIyBSRgoKYGBge3J9CiNSYW5kb20gRm9yZXN0IG1vZGVsCgojQ2hvb3NpbmcgdHVuaW5nIHBhcmFtZXRlcnM6CiNodHRwczovL2Rpc2N1c3MuYW5hbHl0aWNzdmlkaHlhLmNvbS90L2hvdy10by1kZWNpZGUtbm8tb2YtbnRyZWVzLWluLXJhbmRvbWZvcmVzdC82ODgyLzMKI2h0dHBzOi8vcnB1YnMuY29tL3BoYW1kaW5oa2hhbmgvMzg5NzUyCgojQ3JlYXRlIGNvbnRyb2wgZnVuY3Rpb24gZm9yIHRyYWluaW5nIHdpdGggMTAgZm9sZHMgYW5kIGtlZXAgMyBmb2xkcyBmb3IgdHJhaW5pbmcuCnRyYWluX2NvbnRyb2wgPC0gY2FyZXQ6OnRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwLCByZXR1cm5SZXNhbXA9J2FsbCcsIGNsYXNzUHJvYnM9VFJVRSwgc2F2ZVByZWRpY3Rpb25zPSdmaW5hbCcpCgojaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTAwODU4MDYvZXh0cmFjdGluZy1zcGVjaWZpYy1jb2x1bW5zLWZyb20tYS1kYXRhLWZyYW1lCmRmPC0gdHJhaW4gJT4lCiAgc2VsZWN0KFJlZCxHcmVlbixCbHVlKQoKI210cnlTdGFydCBkZWZhdWx0cyBhdCBzcXJ0KHApCiNteSBhdmFpbGFibGUgdGhyZXNob2xkIGZvciBtdHJ5IHZhbHVlcyBpcyBwcmV0dHkgbG93IGJhc2VkIG9uIHRoZSBzaXplIG9mIG15IGRhdGFzZXQgCih0dW5lUkYoZGYsdHJhaW4kQmx1ZUNsYXNzLG10cnkgPSA1LCBudHJlZSA9IDUwMCwgc3RlcEZhY3Rvcj01LCBpbXByb3ZlPTAuMDUsCiAgICAgICB0cmFjZT1UUlVFLCBwbG90PVRSVUUsIGRvQmVzdD1UUlVFKSkKI210cnkgPSAzCgojIy0tLS0tLQoKdHVuZWdyaWQgPC0gZXhwYW5kLmdyaWQoLm10cnkgPSAzKQptb2RlbGxpc3QgPC0gbGlzdCgpCiN0cmFpbiB3aXRoIGRpZmZlcmVudCBudHJlZSBwYXJhbWV0ZXJzIGFuZCBpbnNwZWN0IGJpYXMvdmFyaWFuY2UgdHJhZGVvZmYgCiNmaW5kdHJlZXMxIDwtIHRyYWluKEJsdWVDbGFzc35SZWQrR3JlZW4rQmx1ZSwgCiAjICAgICAgICAgICAgICBkYXRhPXRyYWluLAogICMgICAgICAgICAgICAgbWV0aG9kID0gJ3JmJywKICAgIyAgICAgICAgICAgIG1ldHJpYyA9ICdBY2N1cmFjeScsCiAgICAjICAgICAgICAgICB0dW5lR3JpZCA9IHR1bmVncmlkLAogICAgICMgICAgICAgICAgdHJDb250cm9sID0gY29udHJvbCwKICAgICAgIyAgICAgICAgIG50cmVlID0gNTApCiNmaW5kdHJlZXMxCgojZmluZHRyZWVzMiA8LSB0cmFpbihCbHVlQ2xhc3N+UmVkK0dyZWVuK0JsdWUsIAogIyAgICAgICAgICAgICAgZGF0YT10cmFpbiwKICAjICAgICAgICAgICAgIG1ldGhvZCA9ICdyZicsCiAgICMgICAgICAgICAgICBtZXRyaWMgPSAnQWNjdXJhY3knLAogICAgIyAgICAgICAgICAgdHVuZUdyaWQgPSB0dW5lZ3JpZCwKICAgICAjICAgICAgICAgIHRyQ29udHJvbCA9IGNvbnRyb2wsCiAgICAgICMgICAgICAgICBudHJlZSA9IDEwMCkKI2ZpbmR0cmVlczIKCnN5c3RlbS50aW1lKHsKUkYgPC0gdHJhaW4oQmx1ZUNsYXNzflJlZCtHcmVlbitCbHVlLCAKICAgICAgICAgICAgICAgZGF0YT10cmFpbiwKICAgICAgICAgICAgICAgbWV0aG9kID0gJ3JmJywKICAgICAgICAgICAgICAgbWV0cmljID0gJ0FjY3VyYWN5JywKICAgICAgICAgICAgICAgdHVuZUdyaWQgPSB0dW5lZ3JpZCwKICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5fY29udHJvbCwKICAgICAgICAgICAgICAgbnRyZWUgPSA1MDApCn0pClJGCmBgYAoKYGBge3J9CiMjZXQgcHJlZGljdGlvbiwgcHJvYmFiaWxpdHksIGFuZCBjdiBzY29yZSB2YXJpYWJsZXMgaW4gY2FzZSBuZWVkZWQKcmZtb2RfcHJlZCA8LSBwcmVkaWN0KFJGLCB0ZXN0LCdyYXcnKQpyZm1vZF9wcm9iIDwtIHByZWRpY3QoUkYsIHRlc3QsJ3Byb2InKQpyZm1vZF9zY29yZWQgPC0gY2JpbmQodGVzdCwgcmZtb2RfcHJlZCwgcmZtb2RfcHJvYikKYGBgCgpgYGB7cn0KI0FVQy9ST0MKb3B0aW9ucyh5YXJkc3RpY2suZXZlbnRfZmlyc3Q9RkFMU0UpCgojYXJlYSB1bmRlciB0aGUgY3VydmUKcmZfYXVjID0gcmZtb2RfcHJvYiAlPiUKICB5YXJkc3RpY2s6OnJvY19hdWModHJ1dGg9dGVzdCRCbHVlQ2xhc3MsIFllcykKcmZfYXVjCgojUk9DIGN1cnZlICsgcGxvdApST0NfY3VydmU0PC1yZm1vZF9wcm9iICU+JQogIHlhcmRzdGljazo6cm9jX2N1cnZlKHRydXRoPXRlc3QkQmx1ZUNsYXNzLGVzdGltYXRlPVllcykgJT4lCiAgZHBseXI6Om11dGF0ZShvbmVfbWludXNfc3BlY2lmaWNpdHkgPSAxLXNwZWNpZmljaXR5KQoKUk9DX2N1cnZlX3Bsb3Q0IDwtIFJPQ19jdXJ2ZTQgJT4lCiAgZ2dwbG90KGFlcyh4PW9uZV9taW51c19zcGVjaWZpY2l0eSx5PXNlbnNpdGl2aXR5KSkrCiAgZ2VvbV9saW5lKCkgKyBnZW9tX3BvaW50KCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSxpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZT0nZGFzaGVkJyxjb2xvcj0nYmx1ZScpKwogIHhsYWIoIm9uZV9taW51c19zcGVjaWZpY2l0eVxuKGZhbHNlIHBvc2l0aXZlIHJhdGUpIikrCiAgZ2d0aXRsZSgnTERBIFJPQyBjdXJ2ZScpCmdncGxvdGx5KFJPQ19jdXJ2ZV9wbG90NCkKYGBgCgpgYGB7cn0KI3NldCBuZXcgdGhyZXNob2xkCnJmbW9kX3ByZWQyIDwtIFJGJHByZWQgJT4lIAogICN0aGUgYWNjdXJhY3kgZG9lc24ndCBpbXByb3ZlIGJ5IHJlZHVjaW5nIHRoZSB0aHJlc2hvbGQgYW55IGZ1cnRoZXIgdGhhbiAuNTAsIDk5LjclIGJlc3QuCiAgbXV0YXRlKHByZWRpY3Rpb24gPSBpZmVsc2UoWWVzPi41MCwgJ1llcycsICdObycpKSAlPiUKICBtdXRhdGUocHJlZGljdGlvbiA9IGZhY3RvcihwcmVkaWN0aW9uLCBsZXZlbHM9YygnTm8nLCdZZXMnKSkpCgojbmV3IHRocmVzaG9sZCBtYXRyaXgKY29uZnVzaW9uTWF0cml4KHJmbW9kX3ByZWQyJHByZWRpY3Rpb24sIHJmbW9kX3ByZWQyJG9icywgcG9zaXRpdmU9IlllcyIpCmBgYAoKIyMjIyMgU1ZNCgpgYGB7cn0KI0Nob29zaW5nIHR1bmluZyBwYXJhbWV0ZXJzOgojTGluZWFyCnNldC5zZWVkKDEpCnR1bmUub3V0LmxpbmVhcj10dW5lKHN2bSxCbHVlQ2xhc3N+UmVkK0dyZWVuK0JsdWUsZGF0YT10cmFpbixrZXJuZWw9ImxpbmVhciIscmFuZ2VzPWxpc3QoY29zdD1jKDAuMDAxLCAwLjAxLCAwLjEsIDEsNSwxMCwxMDApKSkKCiNSYWRpYWwKdHVuZS5vdXQucmFkaWFsPXR1bmUoc3ZtLCBCbHVlQ2xhc3N+UmVkK0dyZWVuK0JsdWUsZGF0YT10cmFpbiwga2VybmVsPSJyYWRpYWwiLCByYW5nZXM9bGlzdChjb3N0PWMoMC4xLDEsMTAsMTAwLDEwMDApLGdhbW1hPWMoMC41LDEsMiwzLDQpKSkKI2xvd2VzdCBlcnJvciBpcyByYWRpYWwgY29zdCAxMCBnYW1tYSAxCgojUG9seQp0dW5lLm91dC5wb2x5PXR1bmUoc3ZtLEJsdWVDbGFzc35SZWQrR3JlZW4rQmx1ZSxkYXRhPXRyYWluLCBrZXJuZWw9InBvbHlub21pYWwiLCByYW5nZXM9bGlzdChjb3N0PWMoMC4xLDEsMTAsMTAwLDEwMDApLGRlZ3JlZT1jKDEsMiwzLDQsNSkpKQoKIyMtLS0tLS0KCnN5c3RlbS50aW1lKHsKc3ZtbW9kIDwtIHRyYWluKEJsdWVDbGFzc35SZWQrR3JlZW4rQmx1ZSwgCiAgICAgICAgICAgICAgIGRhdGE9dHJhaW4sCiAgICAgICAgICAgICAgIG1ldGhvZCA9ICdzdm1SYWRpYWwnLAogICAgICAgICAgICAgICBtZXRyaWMgPSAnQWNjdXJhY3knLAogICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sLAogICAgICAgICAgICAgICBjb3N0ID0gMTAsCiAgICAgICAgICAgICAgIGdhbW1hID0gMSwKICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsInNjYWxlIikKICAgICAgICAgICAgICAgKQp9KQoKYGBgCgpgYGB7cn0KI1NldCBwcmVkaWN0aW9uLCBwcm9iYWJpbGl0eSwgYW5kIGN2IHNjb3JlIHZhcmlhYmxlcyBpbiBjYXNlIG5lZWRlZApzdm1tb2RfcHJlZCA8LSBwcmVkaWN0KHN2bW1vZCwgdGVzdCwncmF3JykKc3ZtbW9kX3Byb2IgPC0gcHJlZGljdChzdm1tb2QsIHRlc3QsJ3Byb2InKQpzdm1tb2Rfc2NvcmVkIDwtIGNiaW5kKHRlc3QsIHN2bW1vZF9wcmVkLCBzdm1tb2RfcHJvYikKYGBgCgpgYGB7cn0KI0FVQy9ST0MKb3B0aW9ucyh5YXJkc3RpY2suZXZlbnRfZmlyc3Q9RkFMU0UpCgojYXJlYSB1bmRlciB0aGUgY3VydmUKc3ZtbW9kX2F1YyA9IHN2bW1vZF9wcm9iICU+JQogIHlhcmRzdGljazo6cm9jX2F1Yyh0cnV0aD10ZXN0JEJsdWVDbGFzcywgWWVzKQpzdm1tb2RfYXVjCgojUk9DIGN1cnZlICsgcGxvdApST0NfY3VydmU0PC1zdm1tb2RfcHJvYiAlPiUKICB5YXJkc3RpY2s6OnJvY19jdXJ2ZSh0cnV0aD10ZXN0JEJsdWVDbGFzcyxlc3RpbWF0ZT1ZZXMpICU+JQogIGRwbHlyOjptdXRhdGUob25lX21pbnVzX3NwZWNpZmljaXR5ID0gMS1zcGVjaWZpY2l0eSkKClJPQ19jdXJ2ZV9wbG90NCA8LSBST0NfY3VydmU0ICU+JQogIGdncGxvdChhZXMoeD1vbmVfbWludXNfc3BlY2lmaWNpdHkseT1zZW5zaXRpdml0eSkpKwogIGdlb21fbGluZSgpICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX2FibGluZShzbG9wZSA9IDEsaW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9J2Rhc2hlZCcsY29sb3I9J2JsdWUnKSsKICB4bGFiKCJvbmVfbWludXNfc3BlY2lmaWNpdHlcbihmYWxzZSBwb3NpdGl2ZSByYXRlKSIpKwogIGdndGl0bGUoJ0xEQSBST0MgY3VydmUnKQpnZ3Bsb3RseShST0NfY3VydmVfcGxvdDQpCgpgYGAKCmBgYHtyfQojc2V0IG5ldyB0aHJlc2hvbGQKc3ZtbW9kX3ByZWQyIDwtIHN2bW1vZCRwcmVkICU+JSAKICAjdGhlIGFjY3VyYWN5IGRvZXNuJ3QgaW1wcm92ZSBieSByZWR1Y2luZyB0aGUgdGhyZXNob2xkIGFueSBmdXJ0aGVyIHRoYW4gLjUyLCA5OS43JSBiZXN0LgogIG11dGF0ZShwcmVkaWN0aW9uID0gaWZlbHNlKFllcz4uNTIsICdZZXMnLCAnTm8nKSkgJT4lCiAgbXV0YXRlKHByZWRpY3Rpb24gPSBmYWN0b3IocHJlZGljdGlvbiwgbGV2ZWxzPWMoJ05vJywnWWVzJykpKQoKI25ldyB0aHJlc2hvbGQgbWF0cml4CmNvbmZ1c2lvbk1hdHJpeChzdm1tb2RfcHJlZDIkcHJlZGljdGlvbiwgc3ZtbW9kX3ByZWQyJG9icywgcG9zaXRpdmU9IlllcyIpCmBgYAoKCgojIyMjIyBUdW5pbmcgcGFyYW1ldGVyIHNlbGVjdGlvbiBwcm9jZXNzCgooYSkgSW50ZXJwcmV0YXRpb24gb2YgeW91ciBjaG9zZW4gdHVuaW5nIHBhcmFtZXRlciB2YWx1ZXM6ICAKICArIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPk50cmVlID0gNTAwIGluZGljYXRlcyB0aGF0IDUwMCB0cmVlcyB3ZXJlIGNyZWF0ZWQuIEkgdW5kZXJzdGFuZCB0aGF0IGl0IGlzIGltcG9ydGFudCBmb3IgdGhpcyBudW1iZXIgdG8gYmUgc3Vic3RhbnRpYWwgdG8gcmVkdWNlIHZhcmlhbmNlLCBidXQgYXMgd2Uga25vdywgdGhhdCBjYW4gYWxzbyBsZWFkIHRvIGJpYXMuIFRoZXJlIHNob3VsZCBiZSBhIHN3ZWV0IHNwb3Qgd2hlcmUgZXZlcnkgaW5wdXQgcm93IGdldHMgcHJlZGljdGVkIGF0IGxlYXN0IGEgZmV3IHRpbWVzIHdpdGhvdXQgb3ZlcmZpdHRpbmcgdGhlIHRyZWVzLiBNdHJ5PTUgaW5kaWNhdGVzIHRoYXQgNSB2YXJpYWJsZXMgd2VyZSByYW5kb21seSBzYW1wbGVkIGF0IGVhY2ggc3BsaXQuIFdoZW4gbXRyeT1wIGl0IGNhbiBlc3NlbnRpYWxseSBlcXVhdGUgdG8gYmFnZ2luZywgd2hlcmVhcyBpZiBpdHMgc2V0IHRvIDEgaXQgZXNzZW50aWFsbHkgY2hvb3NlcyBhIHJhbmRvbSB2YXJpYWJsZS4gSSB1bmRlcnN0YW5kIGl0IGlzIGdvb2QgdG8gdHJ5IG91dCBhIGZldyB2YWx1ZXMgdGhhdCByYW5nZSBubyBzbWFsbGVyIHRoYW4gMiBhbmQgbm8gbGFyZ2VyIHRoYW4gcC4gVGhlIGdhbW1hPTEgc3ZtIHBhcmFtZXRlciBpbmRpY2F0ZXMgdGhhdCBhIHNpbmdsZSB0cmFpbmluZyBleGFtcGxlIGhhcyBmYXIgcmVhY2hpbmcgaW5mbHVlbmNlLiBBIGNvc3QgPSAxMCBoZXJlIG1lYW5zIHdlIGFyZSAicGF5aW5nIiBhIGhpZ2ggcHJpY2UgZm9yIGhpZ2hlciBhY2N1cmFjeS48L3NwYW4+ICAKICAKKGIpIEV4cGxhbmF0aW9uIG9mIGhvdyB5b3VyIGNob3NlbiB0dW5pbmcgcGFyYW1ldGVyIHZhbHVlcyB3ZXJlIGNob3NlbjogIAogICsgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+SSB0cmllZCB0byBtYWtlIHRoaXMgc2VsZWN0aW9uIHByb2Nlc3MgYXMgcHJvZ3JhbW1hdGljIGFzIHBvc3NpYmxlLiBUaGVyZSB3YXMgYSBsb3Qgb2YgbGVhcm5pbmcgYW5kIEknbSBzdXJlIHRoZXJlIGlzIHN0aWxsIGEgaGVhbHRoeSBhbW91bnQgdGhhdCBzaG91bGQgYmUgY29ycmVjdGVkIHdpdGhpbiB0aGUgY29kZSBidXQgSSB3YXMgZ2xhZCB0byBmaW5kIHRoZSByZXNvdXJjZXMgdGhhdCBJIGRpZC4gRm9yIG10cnkgSSB1c2VkIHRoZSB0dW5lUkYoKSBmdW5jdGlvbiB0aGF0IGFjY2VwdHMgYW4gaW5pdGlhbCB2YWx1ZSBmb3IgbXRyeSBhbmQgcmV0dXJucyB0aGUgb3V0LW9mLWJhZyBlcnJvciBmb3IgeW91ciBpbnB1dCB2YWx1ZSBhcyB3ZWxsIGFzIGEgZmV3IHN1cnJvdW5kaW5nIHZhbHVlcy4gSSBjaG9zZSBtdHJ5IGJlY2F1c2UgaXQgcHJvZHVjZWQgdGhlIGxvd2VzdCBvb2IgZXJyb3IgYW5kIHdlbnQgb24gdG8gY2hvb3NlIG50cmVlcyBmcm9tIHRoZXJlLiBBdCB0aGF0IHBvaW50IEkgaW5jbHVkZWQgbXkgbXRyeT0zIGludG8gc2V2ZXJhbCBSRiBtb2RlbHMgd2l0aCBudHJlZXMgb2YgZGlmZmVyZW50IHZhbHVlcyBhbmQgYWdhaW4gbG9va2VkIGZvciB0aGUgaGlnaGVzdCBhY2N1cmFjeS4gTnRyZWVzPTUwMCB3YXMgdGhlIHdpbm5lciBpbiB0aGF0IHNlbnNlLCBhbmQgaXQgYXBwZWFycyB0byBiZSBhIHZlcnkgY29tbW9ubHkgdXNlZCB2YWx1ZSBmb3IgdGhhdCBwYXJhbWV0ZXIuIEZvciB0aGUgU1ZNIHBhcmFtZXRlcnMgSSB1c2VkIG91ciBjbGFzcyBsYWIgYXMgYSBndWlkZSB0byBydW4gdGhlIHR1bmUoKSBmdW5jdGlvbiBmb3IgbGluZWFyLCByYWRpYWwgYW5kIHBvbHlub21pYWwga2VybmVscyB3aXRoIHNldmVyYWwgY29zdCwgZ2FtbWEgYW5kIGRlZ3JlZSB2YWx1ZXMgcmVzcGVjdGl2ZWx5LiBUaGUgdHVuZSBmdW5jdGlvbiByZXR1cm5zIGEgYmVzdCBwZXJmb3JtZXIsIHdoaWNoIHdhcyB0aGUgcmFkaWFsIGtlcm5lbCB3aXRoIGNvc3QgPSAxMCBhbmQgZ2FtbWEgPSAxLiBXaGVuIHJ1bm5pbmcgdGhlIHRyYWluIGZ1bmN0aW9uIHRoZSBzaWdtYSA9IDguNjkxMjYyIGFuZCBDID0gMSB2YWx1ZXMgd2VyZSByZXR1cm5lZCBhcyBjb250cmlidXRpbmcgdG8gdGhlIGhpZ2hlc3QgYWNjdXJhY3kgYW5kIHRoZXJlZm9yZSB3ZXJlIHRoZSBtb3N0IG9wdGltYWwgdmFsdWVzIGZvciB0aG9zZSBwYXJhbWV0ZXJzLiA8L3NwYW4+CiAgCiAgCiMjIyMjIENvbmNsdXNpb25zCiAgCihhKSBBIGRpc2N1c3Npb24gb2YgdGhlIGJlc3QgcGVyZm9ybWluZyBhbGdvcml0aG0ocykgaW4gdGhlIGNyb3NzLXZhbGlkYXRpb24gYW5kIGhvbGQtb3V0IGRhdGEgCiAgKyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5UaGUgYmVzdCBwZXJmb3JtaW5nIGFsZ29yaXRobSBpbiB0aGUgY3Jvc3MgdmFsaWRhdGlvbiBzcGVjdHJ1bSB3b3VsZCBiZSBHTE0gb3IgUURBLiBCb3RoIHJ1biBpbiB1bmRlciAxLjUgc2Vjb25kcyB0byB0aGUgdXNlciAodXAgdG8gfjE1IHNlY29uZHMgZmFzdGVyIHRoYW4gc29tZSBvZiB0aGUgb3RoZXJzKSBhbmQgc3RpbGwgaGF2ZSBhbiBhY2N1cmFjeSBvdmVyIDk5LjQlLiAgVGhlIGhvbGQgb3V0IGRhdGEgZ2VuZXJhbCByYW4gdmVyeSBzbG93bHksIHdoaWNoIGNvdWxkIGJlIGR1ZSB0byB0aGUgc2l6ZSBvZiB0aGUgZGF0YXNldCwgYnV0IGdpdmVuIHRoZSBjb25zaXN0ZW50bHkgaGlnaCBhY2N1cmFjeSBhbmQgZWFzeSBvZiB1c2Ugb2YgdGhlc2UgdHdvIGZ1bmN0aW9ucyBJIHdvdWxkIGJlIGV2ZW4gbW9yZSBsaWtlbHkgdG8gYWR2b2NhdGUgZm9yIHRoZW0gaW4gaG9sZCBvdXQgc2V0dGluZy48L3NwYW4+ICAKCihiKSBBIHJlY29tbWVuZGF0aW9uIGFuZCByYXRpb25hbGUgcmVnYXJkaW5nIHdoaWNoIGFsZ29yaXRobSB0byB1c2UgZm9yIGRldGVjdGlvbiBvZiBibHVlIHRhcnBzIAogICsgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+Rm9yIHRoZSBkZXRlY3Rpb24gb2YgYmx1ZSB0YXJwcyBJ4oCZZCByZWNvbW1lbmQgdXNpbmcgdGhlIHJhZGlhbCBrZXJuZWwgc3ZtIGZ1bmN0aW9uLiBJdHMgdmFsdWFibGUgdG8gdHVuZSB5b3VyIHBhcmFtZXRlcnMgYWNjb3JkaW5nIHRvIHlvdXIgZGF0YSBhbmQgaG93IHlvdSBtYXkgZGVmaW5lIGFjY3VyYWN5LiBQYXJ0aWN1bGFybHkgd2l0aCB0aGlzIHBpeGVsIGRhdGEsIHlvdSBjb3VsZCBsb3dlciB0aGUgdGhyZXNob2xkIGZvciBkZXRlcm1pbmluZyBhIGJsdWUgdGFycCBrbm93aW5nIHRoYXQgaXQgZ29lcyBiZXlvbmQgdGhlIG9yaWdpbmFsIHRhcnAgbGltaXRzLCBidXQgYXZvaWRzIHRoZSBwb3NzaWJpbGl0eSBvZiBuZWdsZWN0aW5nIGEgaHVtYW4gaW4gbmVlZC4gVGhlIHJlYXNvbiBJIGJlY2FtZSBwYXJ0aWFsIHRvIHRoZXNlIGZ1bmN0aW9ucyBpbiB0aGUgY29udGV4dCBvZiBhIG5hdHVyYWwgZGlzYXN0ZXIsIGlzIGJlY2F1c2UgaXQgY2FuIGdpdmUgeW91IGEgc3RhcnRpbmcgcG9pbnQgZm9yIGFkZGl0aW9uYWwgdHVuaW5nLiBBY2N1cmFjeSBhbmQgYXV0b21hdGlvbiBhcmUgdHdvIHRoaW5ncyBJIHdvdWxkIGFzc3VtZSBhcmUgZXh0cmVtZWx5IHZhbHVhYmxlIGluIHRoZSB3YWtlIG9mIGEgbmF0dXJhbCBkaXNhc3Rlci48L3NwYW4+ICAKCihjKSBBZGRpdGlvbmFsIHRob3VnaHRzIGFuZCBxdWVzdGlvbnM6CiAgKyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5J4oCZdmUgZG9uZSBzb21lIHJlc2VhcmNoIHRvIGxlYXJuIG1vcmUgYWJvdXQgaG93IFJHQiBhdmVyYWdlIHRocmVzaG9sZHMgYXJlIHNldCBpbiBvcmRlciB0byBkZXRlcm1pbmUgdGhhdCBpdCBpcyBhIOKAnGdyZWVu4oCdIHZlZ2V0YXRpb24gYXJlYSBvciDigJxyZWTigJ0gc29pbCBhcmVhLiBJIHVuZGVyc3RhbmQgZnJvbSBhIHBhcGVyIHRpdGxlZCDigJxHZW9zcGF0aWFsIERpc2FzdGVyIFJlc3BvbnNlIGR1cmluZyB0aGUgSGFpdGkgRWFydGhxdWFrZTogQSBDYXNlIFN0dWR5IFNwYW5uaW5nIEFpcmJvcm5lIERlcGxveW1lbnQsIERhdGEgQ29sbGVjdGlvbiwgVHJhbnNmZXIsIFByb2Nlc3NpbmcsIGFuZCBEaXNzZW1pbmF0aW9u4oCdIHRoYXQgYSBNYWhhbGFub2JpcyBkaXN0YW5jZSBjbGFzc2lmaWVyIHdhcyB0cmFpbmVkIHVzaW5nIGEgY29sbGVjdGlvbiBvZiBrbm93biBibHVlIHRhcnAgcGl4ZWxzLCB0aGUgUkdCIHZhbHVlcyBvZiB0aGUgcGl4ZWxzIGluIHRoZSBwaG90b3Mgd2VyZSBhdmVyYWdlZCwgYW5kIHRoYXTigJlzIGhvdyB0aGUgb3RoZXIgcGl4ZWxzIGFyZSBkZXRlcm1pbmVkIHRvIGJlIGJsdWUgdGFycCBvciBub3QgdG8gYmUgYmx1ZSB0YXJwLiBNeSBpc3N1ZSBpcyBsYXJnZWx5IHRoYXQgdGhlIGF2ZXJhZ2Ugb2YgdGhvc2UgdGhyZWUgdmFsdWVzIGlzIG5vdCBleGNsdXNpdmUgdG8gdGhlIGNvbG9yLiBJIHJlYWQgdGhhdCDigJxSR0IgKFJlZCwgR3JlZW4sIEJsdWUpIGFyZSA4IGJpdCBlYWNoLiBUaGUgcmFuZ2UgZm9yIGVhY2ggaW5kaXZpZHVhbCBjb2xvdXIgaXMgMC0yNTUgKGFzIDJeOCA9IDI1NiBwb3NzaWJpbGl0aWVzKS4gVGhlIGNvbWJpbmF0aW9uIHJhbmdlIGlzIDI1NioyNTYqMjU2LiBCeSBkaXZpZGluZyBieSAyNTUsIHRoZSAwLTI1NSByYW5nZSBjYW4gYmUgZGVzY3JpYmVkIHdpdGggYSAwLjAtMS4wIHJhbmdlIHdoZXJlIDAuMCBtZWFucyAwICgweDAwKSBhbmQgMS4wIG1lYW5zIDI1NSAoMHhGRiku4oCdIFNvIGhvdyBjb3VsZCB0aGUgYXZlcmFnZSBvZiB0aGVzZSB0aHJlZSB2YWx1ZXMgaW5kaWNhdGUgc29tZXRoaW5nIGlzIGJsdWUgb3ZlciBpdCBiZWluZyByZWQsIHdoZW4gdGhlIGF2ZXJhZ2UgZm9yIHNvbWV0aGluZyBiZWluZyDigJh0cnVl4oCZIHJlZCAoMjU1LDAsMCkgd291bGQgaGF2ZSB0aGUgc2FtZSBhdmVyYWdlIGFzIHNvbWV0aGluZyBiZWluZyDigJh0cnVl4oCZIGdyZWVuIG9yIGJsdWUgYXQgKDAsMjU1LDApIG9yICgwLDAsMjU1KSByZXNwZWN0aXZlbHkuIEl0IHNlZW1zIG1vcmUgbG9naWNhbCBmb3IgdGhlcmUgdG8gYmUgYSBsb3dlciBib3VuZCBsaW1pdCB0byB3aGF0IGNvdWxkIGJlIGNvbnNpZGVyZWQgYmx1ZSwgaW4gYWRkaXRpb24gdG8gaXQgYmVpbmcgYXQgbGVhc3QgMS4yeCB0aGUgdmFsdWVzIG9mIHRoZSBvdGhlciB0d28gZm9yIGV4YW1wbGUuIFRoZXJlIGFyZSBzdHVkaWVzIGFyb3VuZCBhY2Nlc3NpYmlsaXR5IGFuZCBleGFjdGx5IHdoYXQgc2hhZGUgYmVoaW5zIHRvIGJlIHVuc2VlYWJsZSBieSBzb21lb25lIHdobyBpcyBjb2xvciBibGluZCwgaG93IGRvIHRoZXkgZGV0ZXJtaW5lIHRoZWlyIHRocmVzaG9sZHM/IFRoYXQgd2lsbCBiZSBteSBuZXh0IHJhYmJpdCBob2xlLjwvc3Bhbj4KCg==